package com.realone.packman.codec.encoder;


import com.realone.packman.codec.encoder.vo.BmsEncoderHeadVo;
import com.realone.packman.codec.exception.BmsEncoderException;

import java.nio.ByteBuffer;

/**
 * @author hugx
 * @create 2018-09-12 14:33
 */
public abstract class BmsEncoder<BmsEncoderDataVo> {

    private static  short frameNumber;  /* 帧序号 */
    private static Object lock=new Object();

    /**
     * 编码
     *
     * @param bmsEncoderDataVo
     * @return
     */
    protected abstract byte[] encoderData(BmsEncoderDataVo bmsEncoderDataVo);


    /**
     * 请求编码
     *   模板模式
     *  @param cmd 请求指令
     * @param bmsEncoderDataVo
     * @return
     */
    public byte[] encoder(byte cmd,BmsEncoderDataVo bmsEncoderDataVo) {
        ByteBuffer result = null;
        try {

            BmsEncoderHeadVo vo = new BmsEncoderHeadVo();
            byte[] head = encoderHead(vo);
            byte[] body = encoderBody(cmd,bmsEncoderDataVo);
            byte footer = encoderFooter(vo);
            int len = head.length + body.length + 1;

            result = ByteBuffer.allocate(len);

            result.put(head);
            result.put(body);

            result.put(footer);
            return result.array();
        } catch (Exception e) {
            throw new BmsEncoderException(e);
        }

    }

    /**
     * 请求头编码
     *
     * @return
     */
    private byte[] encoderHead(BmsEncoderHeadVo vo) {
        ByteBuffer bb = ByteBuffer.allocate(4);
        bb.put(vo.getBellwether());
        bb.put(vo.getMerogenesis());
        bb.putShort(calculateFrameNumer()); //帧顺序号

        return bb.array();
    }

    private byte[] encoderBody(byte cmd,BmsEncoderDataVo bmsEncoderDataVo) {
        byte[] datas = encoderData(bmsEncoderDataVo);

        if (datas == null) {
            ByteBuffer bb = ByteBuffer.allocate(2);
            bb.put(cmd);
            bb.put((byte) 0);
            return bb.array();
        } else {
            int dataLen = datas.length;
            ByteBuffer bb = ByteBuffer.allocate(dataLen + 2);
            bb.put(cmd);
            bb.put((byte) dataLen);
            bb.put(datas);
            return bb.array();
        }

    }

    /**
     * 请求尾编码
     *
     * @return
     */
    private byte encoderFooter(BmsEncoderHeadVo vo) {
        return vo.getEndMark();
    }



    /**
     * 帧序号累加器
     *
     * @return
     */
    private short calculateFrameNumer() {
        synchronized (lock) {
            frameNumber++;
        }

        if (frameNumber == 0xFFFF) {
            frameNumber = 0;
        }
        return frameNumber;
    }


}
